package laxproxy

import (
	"encoding/json"
	"errors"
	"fmt"
	"go.uber.org/zap"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/reflect/protoreflect"
	"google.golang.org/protobuf/reflect/protoregistry"
	"laxkits/lib/laxutils"
	"math/rand"
)

// @Desc: 反射获取message实体
// @param pn
// @return proto.Message
func getProtoMessage(pn string) proto.Message {
	if msgRefType, err := protoregistry.GlobalTypes.FindMessageByName(protoreflect.FullName(pn)); err == nil {
		return msgRefType.New().Interface()
	}
	return nil
}

// @Desc: 随机一个枚举值
// @param ed
// @return protoreflect.EnumNumber
func getRandomProtoEnum(ed protoreflect.EnumDescriptor) protoreflect.EnumNumber {
	values := ed.Values()
	if values.Len() > 0 {
		return values.Get(rand.Int() % values.Len()).Number()
	}
	return 0
}

// @Desc: append一个list
// @param fd
// @param destList
func tryAppendList(fd protoreflect.FieldDescriptor, destList protoreflect.List) {
	switch fd.Kind() {
	case protoreflect.BoolKind:
		destList.Append(protoreflect.ValueOfBool((rand.Int31() & 1) > 0))
	case protoreflect.Int32Kind, protoreflect.Int64Kind:
		destList.Append(protoreflect.ValueOfInt32(rand.Int31()))
	case protoreflect.Uint32Kind, protoreflect.Uint64Kind:
		destList.Append(protoreflect.ValueOfUint32(rand.Uint32()))
	case protoreflect.StringKind:
		destList.Append(protoreflect.ValueOfString(laxutils.RandomString(rand.Int()%20 + 1)))
	case protoreflect.EnumKind:
		destList.Append(protoreflect.ValueOfEnum(getRandomProtoEnum(fd.Enum())))
	case protoreflect.FloatKind:
		destList.Append(protoreflect.ValueOfFloat32(rand.Float32()))
	case protoreflect.BytesKind:
		destList.Append(protoreflect.ValueOfBytes([]byte(laxutils.RandomString(rand.Int()%20 + 1))))
	case protoreflect.MessageKind:
		if subMsg := getProtoMessage(string(fd.Message().FullName())); subMsg != nil {
			tryFillMessage(subMsg)
			destList.Append(protoreflect.ValueOfMessage(subMsg.ProtoReflect()))
		}
	default:
		zap.S().Warnf("未知类型:%v,%s", fd.Kind(), fd.Name())
	}
}

// @Desc: 填充message
// @param msg
func tryFillMessage(msg proto.Message) {
	descFields := msg.ProtoReflect().Descriptor().Fields()
	for i := 0; i < descFields.Len(); i++ {
		field := descFields.Get(i)
		if field.IsList() {
			listField := msg.ProtoReflect().Mutable(field).List()
			tryAppendList(field, listField)
			continue
		}
		switch field.Kind() {
		case protoreflect.BoolKind:
			msg.ProtoReflect().Set(field, protoreflect.ValueOfBool((rand.Int31()&1) > 0))
		case protoreflect.Int32Kind, protoreflect.Int64Kind:
			msg.ProtoReflect().Set(field, protoreflect.ValueOfInt32(rand.Int31()))
		case protoreflect.Uint32Kind, protoreflect.Uint64Kind:
			msg.ProtoReflect().Set(field, protoreflect.ValueOfUint32(rand.Uint32()))
		case protoreflect.StringKind:
			msg.ProtoReflect().Set(field, protoreflect.ValueOfString(laxutils.RandomString(rand.Int()%20+1)))
		case protoreflect.EnumKind:
			msg.ProtoReflect().Set(field, protoreflect.ValueOfEnum(getRandomProtoEnum(field.Enum())))
		case protoreflect.FloatKind:
			msg.ProtoReflect().Set(field, protoreflect.ValueOfFloat32(rand.Float32()))
		case protoreflect.BytesKind:
			msg.ProtoReflect().Set(field, protoreflect.ValueOfBytes([]byte(laxutils.RandomString(rand.Int()%20+1))))
		case protoreflect.MessageKind:
			if subMsg := getProtoMessage(string(field.Message().FullName())); subMsg != nil {
				tryFillMessage(subMsg)
				msg.ProtoReflect().Set(field, protoreflect.ValueOfMessage(subMsg.ProtoReflect()))
			}
		default:
			zap.S().Warnf("未知类型:%v, %s.%s", field.Kind(), msg.ProtoReflect().Descriptor().Name(), field.Name())
		}
	}
}

// @Desc: 填充proto
// @param pn proto_name
// @return string[json]
func TryGenerateProtoStr(pn string) string {
	if msgEntity := getProtoMessage(pn); msgEntity != nil {
		tryFillMessage(msgEntity)
		if ret, err := json.MarshalIndent(&msgEntity, "", "    "); err == nil {
			//zap.S().Info(string(ret))
			return string(ret)
		}
	}
	return "Unknown Type " + pn
}

// / 解析web发过来的string，byte给服务器
func TryParseProtoFromString(pn string, srcStr string) ([]byte, error) {
	if msgEntity := getProtoMessage(pn); msgEntity != nil {
		if err := json.Unmarshal([]byte(srcStr), msgEntity); err != nil {
			return []byte(""), err
		}
		if ret, err := proto.Marshal(msgEntity); err != nil {
			return []byte(""), err
		} else {
			return ret, nil
		}
	}
	return []byte(""), errors.New(fmt.Sprintf("UnknownType:%s", pn))
}

// / 解析服务器发过来的byte，json化给web
func TryParseProtoFromByte(pn string, srcStr []byte) (string, error) {
	if msgEntity := getProtoMessage(pn); msgEntity != nil {
		if err := proto.Unmarshal(srcStr, msgEntity); err != nil {
			return "", err
		}
		if ret, err := json.MarshalIndent(&msgEntity, "", "    "); err == nil {
			return string(ret), nil
		}
	}
	return "", errors.New(fmt.Sprintf("UnknownType:%s", pn))
}
